#include "tree.h" 
#include <commctrl.h>
#include "exceptions.h"

namespace win
{
	using std::runtime_error;
	using std::list;

	TreeViewItem::TreeViewItem()
	{
		::ZeroMemory(this,sizeof(TV_ITEM));
	}
	
	TreeViewItem::TreeViewItem(HWND window, HTREEITEM h)
	{
		::ZeroMemory(this,sizeof(TV_ITEM));
		hItem = h;
		mask = TVIF_TEXT | TVIF_PARAM | TVIF_STATE;
		TreeView_GetItem(window,this);
	}

	const char * TreeViewItem::GetText() const
 	{
 		return pszText;
  	}	
  	
  	bool TreeViewItem::IsExpanded() const
  	{
  		return ((state & TVIS_EXPANDED) == TVIS_EXPANDED);
    		
  	}
  	
	void TreeViewItem::SetText(const char * text)
	{
		mask |= TVIF_TEXT;
		pszText = const_cast<char *> (text);
	}
	
	void TreeViewItem::SetData(void * data)
	{
		mask |= TVIF_PARAM;
		lParam = reinterpret_cast<LPARAM> (data);
	}

	void * TreeViewItem::GetData() const
 	{
 		return reinterpret_cast<void*> (lParam);
  	}	
  	
	void TreeViewItem::Clear()
	{
		::ZeroMemory(this,sizeof(TV_ITEM));
	}

	void TreeItem::GetView(TreeViewItem &item)
	{
		item.mask = TVIF_TEXT | TVIF_PARAM;
		item.hItem = _handle;
		TreeView_GetItem(_hwnd,&item);
		item.mask = 0;
	}

	void TreeItem::SetView(TreeViewItem &item)
	{
  		TreeView_SetItem(_hwnd,&item);
	}

	TreeItem::TreeItem()
	{
		_handle = 0;
		_hwnd = 0;
	}
	
	TreeItem::TreeItem(HWND wh, HTREEITEM h)
	{
		_handle = h;
		_hwnd = wh;
	}
	
	TreeItem TreeItem::Append(const TreeViewItem &item)
	{
		TV_INSERTSTRUCT ins;
		ins.hParent = _handle;
		ins.hInsertAfter = TVI_LAST;
		ins.item = item;
		return TreeItem(_hwnd,TreeView_InsertItem(_hwnd,&ins));
	}
	
	TreeItem TreeItem::Parent()
	{
		return TreeItem(_hwnd,TreeView_GetParent(_hwnd,_handle));
	}
	
	void TreeItem::DeleteItem(TreeItem item)
 	{
 		TreeView_DeleteItem(_hwnd,item._handle);
 	}
 
	
	void TreeItem::GetChilds(list<HTREEITEM> &lst)
	{
		HTREEITEM child = TreeView_GetChild(_hwnd,_handle);
		while (child)
		{
			lst.push_back(child);
			child = TreeView_GetNextSibling(_hwnd,child);
		}
	}
	
	bool TreeItem::SelectChildByData(const void* data)
	{
		/// first find the child
		
		list<HTREEITEM> childs;
		GetChilds(childs);
  		list<HTREEITEM>::iterator it = childs.begin();
  		while (it != childs.end())
  		{
     		TreeViewItem tvi(_hwnd,*it);
     		if (tvi.GetData() == data)
     			break;
  			it++;
  		}
  		if (it == childs.end())
  			return false;
		TreeView_Select(_hwnd,*it,TVGN_CARET);
		return true;
	}
	
	bool TreeItem::IsExpanded() const
	{
		const TreeViewItem tvi(_hwnd,_handle);
		return tvi.IsExpanded();
	}
	

	void TreeItem::DeleteChilds()
	{
		list<HTREEITEM> childs;
		GetChilds(childs);
  		list<HTREEITEM>::iterator it = childs.begin();
  		while (it != childs.end())
  		{
     		TreeView_DeleteItem(_hwnd,*it);
  			it++;
  		}
	}
	
	void TreeItem::Expand()
	{
		TreeView_Expand(_hwnd,_handle,TVE_EXPAND);
	}
	
	Tree::Tree()
	{
		_root_item = 0;
	}

	Tree::~Tree()
	{

	}
	
	TreeItem Tree::GetRoot()
	{
		return TreeItem(_hwnd,_root_item);
	}
	
	void Tree::SetRoot(const TreeViewItem &item)
	{
		TV_INSERTSTRUCT ins;
		ins.hParent = TVI_ROOT;
		ins.hInsertAfter = TVI_FIRST;
		ins.item = item;
		_root_item = TreeView_InsertItem(_hwnd,&ins);
	}

	void Tree::Create(Window &parent, const int child_index)
	{
		HINSTANCE hinst = parent.AppInstance();
	    _hwnd = CreateWindowEx(0, WC_TREEVIEW, (LPSTR) NULL,
    	    WS_CHILD |  TVS_HASLINES | WS_VISIBLE | TVS_LINESATROOT | WS_TABSTOP |
         	TVS_HASBUTTONS | TVS_SHOWSELALWAYS ,
       	 	0, 0, 0, 0, parent.GetHandle(), (HMENU) child_index,
    		hinst, NULL);
    	if (_hwnd == 0)
      		throw runtime_error("CreateWindowEx failed");
	}
	
	
	HTREEITEM Tree::ItemAt(const Point &p)
	{
		TV_HITTESTINFO info;
		info.pt = p;
		TreeView_HitTest(_hwnd,&info);
		if (info.flags & TVHT_ONITEM)
			return info.hItem;
		else
			return 0;
	}
	
	
	TreeItem Tree::GetFocusItem()
	{
		return TreeItem(_hwnd,TreeView_GetSelection(_hwnd));
	}
	
	void TreeItem::Select()
	{
		TreeView_Select(_hwnd,_handle,TVGN_CARET);
	}

	void Tree::Select(HTREEITEM hItem)
	{
		TreeView_Select(_hwnd,hItem,TVGN_CARET);
	}
	
	void Tree::DeleteAllItems()
	{
		TreeView_DeleteAllItems(_hwnd);
	}
	
}
